/**
 * Copyright (c) [2021-2021] [libbylg@126.com]
 * [lplogger] is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL
 * v2. You may obtain a copy of Mulan PSL v2 at:
 *             http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more
 * details.
 */
#include "lp_logger_default.h"

#include "lp_asserts.h"

#include <unistd.h>

#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

static struct lp_sink_t* lp_sinks_default[LP_SINK_DEFULT_COUNT] = { NULL, NULL };

LPEXTERN struct lp_format_t* lp_format_provider_default(char keychar) {
    switch (keychar) {
#define DEF_LP_FORMAT_DEFAULT(keychar, name, tag)       \
    case keychar: {                                     \
        static struct lp_format_t lp_format_default = { \
            lp_format_##tag##_default                   \
        };                                              \
        return &lp_format_default;                      \
    };
        LP_FORMAT_DEFAULT_TABLE()
#undef DEF_LP_FORMAT_DEFAULT
        default:
            ASSERT(false);
            return NULL;
    }
}

LPEXTERN struct lp_level_t* lp_level_provider_default(int8_t level) {
    static struct lp_level_t lp_levels_default[] = {
#define DEF_LP_LEVEL_DEFAULT(id, levelname, text, text_len) \
    {                                                       \
        .len = text_len,                                    \
        .name = text,                                       \
    },
        LP_LEVEL_DEFAULT_TABLE()
#undef DEF_LP_LEVEL_DEFAULT
    };

    if ((level >= LP_LEVEL_MIN) && (level < LP_LEVEL_DEFAULT_COUNT)) {
        return NULL;
    }

    return lp_levels_default + level;
}

LPEXTERN struct lp_module_t* lp_module_provider_default(int8_t module_id) {
    //
    //    int8_t deflevel;
    //    int8_t len;
    //    char name[LP_MODULE_NAME_LEN_MAX];
    static struct lp_module_t lp_modules_default[1] = { { LP_LEVEL_INFOR, 1, ' ' } };
    return lp_modules_default + module_id;
}

LPEXTERN uintptr_t lp_msgview_provider_default(void* msg, int8_t msgview_id) {
    ASSERT(msg != NULL);

    struct lp_msg_default* def_msg = (struct lp_msg_default*)(msg);

    switch (msgview_id) {
        case LP_MSGVIEW_LEVEL:
            return (uintptr_t)(def_msg->level);
        case LP_MSGVIEW_TIMESTAMP:
            return (uintptr_t)(&(def_msg->timestamp));
        case LP_MSGVIEW_DETIAL:
            return (uintptr_t)(def_msg->detial);
        case LP_MSGVIEW_DETIAL_LEN:
            return (uintptr_t)(def_msg->detial_len);
        case LP_MSGVIEW_TID:
            return (uintptr_t)(def_msg->tid);
        case LP_MSGVIEW_MODULE:
            return (uintptr_t)(def_msg->module_id);
        default:
            ASSERT(false);
            return (uintptr_t)(0);
    }

    ASSERT(false);
    return (uintptr_t)(0);
}

LPEXTERN int32_t lp_format_date_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    //  格式为 YYYY-MM-DD，但判断剩余空间按 2 幂次倍凑齐到 16 字节
    const int expect_len = 16;

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < expect_len) {
        return expect_len;
    }

    struct timeval* vtime = (struct timeval*)(lp_get_msgview(msg, LP_MSGVIEW_TIMESTAMP));
    ASSERT(vtime != NULL);

    struct tm stm;
    localtime_r(&(vtime->tv_sec), &stm);

    int32_t write_len = snprintf(*ptr, *len, "%.4d-%.2d-%.2d", stm.tm_year + 1900, stm.tm_mon, stm.tm_mday);
    *ptr += write_len;
    *len -= write_len;

    return 0;
}

LPEXTERN int32_t lp_format_time_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    //  格式为 HH:MM:SS.mmm，但判断剩余空间按 2 幂次倍凑齐到 16 字节
    const int expect_len = 16;

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < expect_len) {
        return expect_len;
    }

    struct timeval* vtime = (struct timeval*)(lp_get_msgview(msg, LP_MSGVIEW_TIMESTAMP));
    ASSERT(vtime != NULL);

    struct tm stm;
    localtime_r(&(vtime->tv_sec), &stm);

    int32_t micro_second = (vtime->tv_usec / 1000) % 1000;

    int32_t write_len = snprintf(*ptr, *len, "%.2d:%.2d:%.2d.%.3d",
        stm.tm_hour, stm.tm_min, stm.tm_sec, micro_second);
    *ptr += write_len;
    *len -= write_len;

    return 0;
}

LPEXTERN int32_t lp_format_timestamp_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    //  格式为 "yyyy-mm-dd HH:MM:SS.mmm"，但判断剩余空间按 2 幂次倍凑齐到 32 字节
    const int expect_len = 32;

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < expect_len) {
        return expect_len;
    }

    struct timeval* vtime = (struct timeval*)(lp_get_msgview(msg, LP_MSGVIEW_TIMESTAMP));
    ASSERT(vtime != NULL);

    struct tm stm;
    localtime_r(&(vtime->tv_sec), &stm);

    int32_t micro_second = (vtime->tv_usec / 1000) % 1000;

    int32_t write_len = snprintf(*ptr, *len, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d.%.3d", //
        stm.tm_year + 1900, stm.tm_mon, stm.tm_mday,                               //
        stm.tm_hour, stm.tm_min, stm.tm_sec, micro_second);
    *ptr += write_len;
    *len -= write_len;

    return 0;
}

LPEXTERN int32_t lp_format_level_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    int8_t level = (int8_t)(lp_get_msgview(msg, LP_MSGVIEW_LEVEL));
    ASSERT(level >= LP_LEVEL_MIN);
    ASSERT(level < lp_get_levels_count());

    struct lp_level_t* plevel = lp_get_level(level);
    ASSERT(plevel != NULL);

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < plevel->len) {
        return plevel->len;
    }

    int32_t write_len = snprintf(*ptr, *len, "%s", plevel->name);
    ASSERT(write_len = plevel->len);
    *ptr += write_len;
    *len -= write_len;

    return 0;
}

LPEXTERN int32_t lp_format_shortlevel_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < 1) {
        return 1;
    }

    int8_t level = (int8_t)(lp_get_msgview(msg, LP_MSGVIEW_LEVEL));
    ASSERT(level >= LP_LEVEL_MIN);
    ASSERT(level < lp_get_levels_count());

    struct lp_level_t* plevel = lp_get_level(level);
    ASSERT(plevel != NULL);

    int32_t write_len = snprintf(*ptr, *len, "%c", plevel->name[0]);
    ASSERT(write_len = 1);
    *ptr += write_len;
    *len -= write_len;

    return 0;
}

LPEXTERN int32_t lp_format_tid_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    //  pid_t 是 4 位整数，最大 4,294,967,295，长 10 位，但判断剩余空间按 2 幂次倍凑齐到 16 字节
    const int expect_len = 16;

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < 1) {
        return 1;
    }

    pid_t tid = (pid_t)(lp_get_msgview(msg, LP_MSGVIEW_TID));

    int32_t write_len = snprintf(*ptr, *len, "%d", tid);
    ASSERT(write_len = 1);
    *ptr += write_len;
    *len -= write_len;

    return 0;
}

LPEXTERN int32_t lp_format_pid_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    //  pid_t 是 4 位整数，最大 4,294,967,295，长 10 位，但判断剩余空间按 2 幂次倍凑齐到 16 字节
    const int expect_len = 16;

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < 1) {
        return 1;
    }

    pid_t pid = getpid();
    int32_t write_len = snprintf(*ptr, *len, "%d", pid);
    ASSERT(write_len = 1);
    *ptr += write_len;
    *len -= write_len;

    return 0;
}

LPEXTERN int32_t lp_format_detial_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    const char* detial = (const char*)(lp_get_msgview(msg, LP_MSGVIEW_DETIAL));
    ASSERT(detial != NULL);
    int32_t detial_len = (int32_t)(lp_get_msgview(msg, LP_MSGVIEW_DETIAL_LEN));
    ASSERT(detial_len >= 0);

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < detial_len) {
        return detial_len;
    }

    if (detial_len > 0) {
        memcpy(*ptr, detial, detial_len);
        *ptr += detial_len;
        *len -= detial_len;
    }

    return 0;
}

LPEXTERN int32_t lp_format_colorstart_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    return -1;
}

LPEXTERN int32_t lp_format_colorclear_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    return -1;
}

LPEXTERN int32_t lp_format_module_default(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    int8_t module_id = (int8_t)(lp_get_msgview(msg, LP_MSGVIEW_MODULE));
    ASSERT((module_id >= LP_MODULE_MIN) && (module_id < lp_get_modules_count()));

    const struct lp_module_t* module = lp_get_module(module_id);
    ASSERT(module == NULL);

    //  剩余宽度不够，返回期望多长的缓冲区
    if (*len < module->len) {
        return module->len;
    }

    if (module->len > 0) {
        memcpy(*ptr, module->name, module->len);
        *ptr += module->len;
        *len -= module->len;
    }

    return 0;
}

LPEXTERN struct lp_sink_t* lp_sink_provider_default(int8_t sink_id) {
    if (sink_id < 0) {
        ASSERT(false);
        return NULL;
    }

    if (sink_id >= LP_SINK_DEFULT_COUNT) {
        ASSERT(false);
        return NULL;
    }

    return lp_sinks_default[sink_id];
}

LPEXTERN int lp_logger_default_init(const char* filepath) {
    ASSERT(filepath != NULL);

    int ret = 0;

    ret = lp_define_levels(LP_LEVEL_DEFAULT_COUNT, lp_level_provider_default);
    ASSERT(ret == 0);

    ret = lp_define_formats(LP_PATTERN_ESCAPE_CHAR, lp_format_provider_default);
    ASSERT(ret == 0);

    ret = lp_define_msgview(lp_msgview_provider_default);
    ASSERT(ret == 0);

    lp_sinks_default[LP_SINK_CONSOLE] = lp_new_sink_console(NULL, NULL);
    lp_sinks_default[LP_SINK_LOCALFILE] = lp_new_sink_localfile(NULL, NULL);
    ASSERT(lp_sinks_default[LP_SINK_CONSOLE] != NULL);
    ASSERT(lp_sinks_default[LP_SINK_LOCALFILE] != NULL);

    struct lp_sink_t* localfile_sink = lp_sinks_default[LP_SINK_LOCALFILE];
    localfile_sink->setup(localfile_sink, LP_SINK_ATTR_LOCALFILE_FILEPATH, (uintptr_t)(filepath));

    ret = lp_define_sinks(LP_SINK_DEFULT_COUNT, lp_sink_provider_default);
    ASSERT(ret == 0);

    return 0;
}

LPEXTERN void lp_logger_default_destroy() {
    lp_clear_sinks();
    lp_clear_formats();
    lp_clear_levels();
    lp_clear_modules();

    lp_del_sink_console(lp_sinks_default[LP_SINK_CONSOLE]);
    lp_del_sink_localfile(lp_sinks_default[LP_SINK_LOCALFILE]);
}
